local modid = 'webber_the_trainer'
local scrapbookdata = require 'screens/redux/scrapbookdata'

---@alias webbert_life_work_kind
---| 'chopdone' # 砍伐完成
---| 'minedone' # 开采完成
---| 'fish' # 
---| 'honey' # 从蜂箱中采集蜂蜜
---| 'craft' # 制作物品
---| 'bigplant' # 采集巨大植物
---| 'dish' # 料理收获次数
---| 'dig' # 挖掘次数
---| 'till' # 锄地次数
---| 'hammer' # 锤
---| 'net' # 捕虫网使用次数
---| 'td_pigking' # 猪王交易次数
---| 'td_bird' # 鸟笼交易次数
---| 'td_antlion' # 蚁狮交易次数
---| 'farmplant' # 播种次数

---@class webbert_life_event_data
---@field fn_delta fun(player:ent, data:table|nil):(webbert_life_work_kind|nil,integer|nil) # 计算delta,最终加到工作统计中

---@type table<eventID,webbert_life_event_data>
local event_tbl = {
    finishedwork = {
        fn_delta = function (player, data)
            ---@cast data event_data_finishedwork
            local tar = data and data.target
            local act = data and data.action
            if tar then
                if tar:HasTag('tree') then
                    return 'chopdone', 1
                elseif tar:HasTag('boulder') then
                    return 'minedone', 1
                elseif act and act == ACTIONS.DIG then
                    return 'dig', 1
                elseif act and act == ACTIONS.HAMMER then
                    return 'hammer', 1
                elseif act and act == ACTIONS.NET then
                    return 'net', 1
                end
            end
            return nil,nil
        end
    },
    fishingcollect = {
        fn_delta = function (player, data)
            ---@cast data event_data_fishingcollect
            local fish = data and data.fish
            if fish then
                return 'fish', 1
            end
            return nil,nil
        end
    },
    w_l_harvestsomething = {
        fn_delta = function (player, data)
            ---@cast data event_data_w_l_harvestsomething
            local tar = data and data.object
            if tar then
                local num = data.num and data.num > 0 and data.num or 1
                return 'honey', num
            end
            return nil,nil
        end
    },
    builditem = {
        fn_delta = function (player, data)
            ---@cast data event_data_builditem
            return 'craft', 1
        end
    },
    webbert_mod_pick = {
        fn_delta = function (player, data)
            ---@cast data event_data_webbert_mod_pick
            local tar = data and data.plant
            if tar then
                if tar:HasTag('oversized_veggie') then
                    return 'bigplant', 1
                end
            end
            return nil,nil
        end
    },
    learncookbookrecipe = { -- 这个事件是stewer里推的, 虽然不是收获料理, 但是间接能用
        fn_delta = function (player, data)
            ---@cast data event_data_learncookbookrecipe
            return 'dish', 1
        end
    },
    tilling = {
        fn_delta = function (player, data)
            return 'till', 1
        end
    },
    w_l_trade = {
        fn_delta = function (player, data)
            ---@cast data event_data_w_l_trade
            local owner = data and data.owner
            if owner then
                if owner.prefab == 'pigking' then
                    return 'td_pigking', 1
                elseif owner.prefab == 'birdcage' then
                    return 'td_bird', 1
                elseif owner.prefab == 'antlion' then
                    return 'td_antlion', 1
                end
            end
            return nil,nil
        end
    },
    w_l_farmplant = {
        fn_delta = function (player, data)
            return 'farmplant', 1
        end
    },
}

---确保将本地数据挂到theplayer上
---@param inst ent
local function webbert_refer_persistentstring(inst)
    if not inst then return end
    if inst._client_data_for_webbert_life == nil then
        ---@cast inst ent_player
        local stamp = inst.replica.webbert_life and inst.replica.webbert_life:GetTimeStamp()
        local file = 'webbert_life_start_at_'..stamp
        TheSim:GetPersistentString(file, function(load_success, data)
            if load_success and data ~= nil then
                local status, _data = pcall( function() return json.decode(data) end )
                if status and _data then
                    inst._client_data_for_webbert_life = _data
                end
            end
        end)
    end
end
local function marksure_clientdata(userid)
    if not ThePlayer then return end
    if ThePlayer._client_data_for_webbert_life == nil then ThePlayer._client_data_for_webbert_life = {} end
    if ThePlayer._client_data_for_webbert_life[userid] == nil then ThePlayer._client_data_for_webbert_life[userid] = {} end

    if ThePlayer._client_data_for_webbert_life[userid].kill == nil then ThePlayer._client_data_for_webbert_life[userid].kill = {} end
    if ThePlayer._client_data_for_webbert_life[userid].sup_kill == nil then ThePlayer._client_data_for_webbert_life[userid].sup_kill = {} end
    if ThePlayer._client_data_for_webbert_life[userid].work == nil then ThePlayer._client_data_for_webbert_life[userid].work = {} end
    if ThePlayer._client_data_for_webbert_life[userid].wp == nil then ThePlayer._client_data_for_webbert_life[userid].wp = {} end
    if ThePlayer._client_data_for_webbert_life[userid].eat == nil then ThePlayer._client_data_for_webbert_life[userid].eat = {} end
end

---@class ThePlayer: ent
---@field _client_data_for_webbert_life table

AddPlayerPostInit(function (inst)
    ---@class player_temp_data_for_webbert_life_kill
    ---@field kill table<PrefabID,integer>|nil # 临时生涯kill数据
    ---@field sup_kill table<PrefabID,integer>|nil # 临时生涯协助kill数据

    ---@class player_temp_data_for_webbert_life_work
    ---@field work table<eventID,integer>|nil # 临时生涯work数据

    ---@class player_temp_data_for_webbert_life_eat
    ---@field eat table<eventID,integer>|nil # 临时生涯eat数据

    ---@class player_temp_data_for_webbert_life_wp
    ---@field wp table<PrefabID,integer>|nil # 临时生涯wp数据

    ---@class ent_player: ent
    ---@field _temp_data_for_webbert_life_kill player_temp_data_for_webbert_life_kill # 临时生涯kill数据
    ---@field _temp_data_for_webbert_life_work player_temp_data_for_webbert_life_work # 临时生涯work数据
    ---@field _temp_data_for_webbert_life_eat player_temp_data_for_webbert_life_eat # 临时生涯eat数据
    ---@field _temp_data_for_webbert_life_wp player_temp_data_for_webbert_life_wp # 临时生涯wp数据
    ---@field _client_data_for_webbert_life table # 客户端数据

    ---@cast inst ent_player

    if not TheWorld.ismastersim then
        inst:DoTaskInTime(0,function ()
            webbert_refer_persistentstring(inst)
        end)
        return inst
    end

    inst:AddComponent('webbert_life')
    inst.components.webbert_life:MakeSureTimeStamp()

    inst._temp_data_for_webbert_life_kill = {}

    -- 击杀统计
    inst:ListenForEvent('killed',
    ---comment
    ---@param this ent
    ---@param data event_data_killed
    function (this, data)
        local victim = data and data.victim
        if victim and scrapbookdata[victim.prefab] then
            if inst._temp_data_for_webbert_life_kill.kill == nil then
                inst._temp_data_for_webbert_life_kill.kill = {}
            end
            inst._temp_data_for_webbert_life_kill.kill[victim.prefab] = (inst._temp_data_for_webbert_life_kill.kill[victim.prefab] or 0) + 1
            -- 5秒后同步数据
            SUGAR_webber_the_trainer:taskDoInTime(inst, 'taskintime_sync_webbert_life_kill',5,function ()
                for _,v in ipairs(AllPlayers or {}) do
                    if v:IsValid() and v.userid then -- 不是所有玩家都有userid, 因为可能有假人
                        SendModRPCToClient(GetClientModRPC(modid,modid..'life_sync'),v.userid,inst.userid,json.encode(inst._temp_data_for_webbert_life_kill))
                    end
                end

                inst._temp_data_for_webbert_life_kill.kill = nil
            end,true)
        end
    end)
    inst:ListenForEvent('killed_webber_the_trainer_participate',
    ---comment
    ---@param this ent
    ---@param data event_data_killed
    function (this, data)
        local victim = data and data.victim
        if victim and scrapbookdata[victim.prefab] then
            if inst._temp_data_for_webbert_life_kill.sup_kill == nil then
                inst._temp_data_for_webbert_life_kill.sup_kill = {}
            end
            inst._temp_data_for_webbert_life_kill.sup_kill[victim.prefab] = (inst._temp_data_for_webbert_life_kill.sup_kill[victim.prefab] or 0) + 1
            -- 5秒后同步数据
            SUGAR_webber_the_trainer:taskDoInTime(inst, 'taskintime_sync_webbert_life_kill',5,function ()
                for _,v in ipairs(AllPlayers or {}) do
                    if v:IsValid() and v.userid then -- 不是所有玩家都有userid, 因为可能有假人
                        SendModRPCToClient(GetClientModRPC(modid,modid..'life_sync'),v.userid,inst.userid,json.encode(inst._temp_data_for_webbert_life_kill))
                    end
                end

                inst._temp_data_for_webbert_life_kill.sup_kill = nil
            end,true)
        end
    end)
    -- 工作统计
    inst._temp_data_for_webbert_life_work = {}
    for event,tbl in pairs(event_tbl) do
        inst:ListenForEvent(event,function (this, data)
            if inst._temp_data_for_webbert_life_work.work == nil then
                inst._temp_data_for_webbert_life_work.work = {}
            end
            local work_kind,num = tbl.fn_delta(this,data)
            if work_kind and num then
                inst._temp_data_for_webbert_life_work.work[work_kind] = (inst._temp_data_for_webbert_life_work.work[work_kind] or 0) + num
                SUGAR_webber_the_trainer:taskDoInTime(inst, 'taskintime_sync_webbert_life_work',5,function ()
                    for _,v in ipairs(AllPlayers or {}) do
                        if v:IsValid() and v.userid then -- 不是所有玩家都有userid, 因为可能有假人
                            SendModRPCToClient(GetClientModRPC(modid,modid..'life_sync_work'),v.userid,inst.userid,json.encode(inst._temp_data_for_webbert_life_work))
                        end
                    end
                    inst._temp_data_for_webbert_life_work.work = nil
                end,true)
            end
        end)
    end
    -- 进食统计
    inst._temp_data_for_webbert_life_eat = {}
    inst:ListenForEvent('oneat',function (this, data)
        ---@cast data event_data_oneat
        local food_prefab = data and data.food and data.food.prefab
        if food_prefab then
            if inst._temp_data_for_webbert_life_eat.eat == nil then
                inst._temp_data_for_webbert_life_eat.eat = {}
            end
            inst._temp_data_for_webbert_life_eat.eat[food_prefab] = (inst._temp_data_for_webbert_life_eat.eat[food_prefab] or 0) + 1
            SUGAR_webber_the_trainer:taskDoInTime(inst, 'taskintime_sync_webbert_life_eat',5,function ()
                for _,v in ipairs(AllPlayers or {}) do
                    if v:IsValid() and v.userid then -- 不是所有玩家都有userid, 因为可能有假人
                        SendModRPCToClient(GetClientModRPC(modid,modid..'life_sync_eat'),v.userid,inst.userid,json.encode(inst._temp_data_for_webbert_life_eat))
                    end
                end
                inst._temp_data_for_webbert_life_eat.eat = nil
            end,true)
        end
    end)
    -- 武器使用统计
    inst._temp_data_for_webbert_life_wp = {}
    inst:ListenForEvent('onhitother',function (this, data)
        ---@cast data event_data_onhitother
        local wp_prefab = data and data.weapon and data.weapon.prefab
        if wp_prefab then
            if inst._temp_data_for_webbert_life_wp.wp == nil then
                inst._temp_data_for_webbert_life_wp.wp = {}
            end
            inst._temp_data_for_webbert_life_wp.wp[wp_prefab] = (inst._temp_data_for_webbert_life_wp.wp[wp_prefab] or 0) + 1
            SUGAR_webber_the_trainer:taskDoInTime(inst, 'taskintime_sync_webbert_life_wp',5,function ()
                for _,v in ipairs(AllPlayers or {}) do
                    if v:IsValid() and v.userid then -- 不是所有玩家都有userid, 因为可能有假人
                        SendModRPCToClient(GetClientModRPC(modid,modid..'life_sync_wp'),v.userid,inst.userid,json.encode(inst._temp_data_for_webbert_life_wp))
                    end
                end
                inst._temp_data_for_webbert_life_wp.wp = nil
            end,true)
        end
    end)
end)

AddClientModRPCHandler(modid,modid..'life_sync',function (userid, sync_data)
    local stamp = ThePlayer and ThePlayer.replica.webbert_life and ThePlayer.replica.webbert_life:GetTimeStamp()
    if stamp then
        local file = 'webbert_life_start_at_'..stamp
        webbert_refer_persistentstring(ThePlayer)
        if ThePlayer and userid and sync_data then
            marksure_clientdata(userid)
            local status, data = pcall( function() return json.decode(sync_data) end )
            if status and data then
                ---@cast data player_temp_data_for_webbert_life_kill
                for kind,tbl in pairs(data) do
                    for prefab,num in pairs(tbl) do
                        ThePlayer._client_data_for_webbert_life[userid][kind][prefab] = (ThePlayer._client_data_for_webbert_life[userid][kind][prefab] or 0) + num
                    end
                end
                TheSim:SetPersistentString(file, json.encode(ThePlayer._client_data_for_webbert_life), false)
            end
        end
    end
end)

AddClientModRPCHandler(modid,modid..'life_sync_work',function (userid, sync_data)
    local stamp = ThePlayer and ThePlayer.replica.webbert_life and ThePlayer.replica.webbert_life:GetTimeStamp()
    if stamp then
        local file = 'webbert_life_start_at_'..stamp
        webbert_refer_persistentstring(ThePlayer)
        if ThePlayer and userid and sync_data then
            marksure_clientdata(userid)
            local status, data = pcall( function() return json.decode(sync_data) end )
            ---@cast data player_temp_data_for_webbert_life_work
            if status and data and data.work then
                for kind,num in pairs(data.work) do
                    ThePlayer._client_data_for_webbert_life[userid].work[kind] = (ThePlayer._client_data_for_webbert_life[userid].work[kind] or 0) + num
                end
                TheSim:SetPersistentString(file, json.encode(ThePlayer._client_data_for_webbert_life), false)
            end
        end
    end
end)

AddClientModRPCHandler(modid,modid..'life_sync_eat',function (userid, sync_data)
    local stamp = ThePlayer and ThePlayer.replica.webbert_life and ThePlayer.replica.webbert_life:GetTimeStamp()
    if stamp then
        local file = 'webbert_life_start_at_'..stamp
        webbert_refer_persistentstring(ThePlayer)
        if ThePlayer and userid and sync_data then
            marksure_clientdata(userid)
            local status, data = pcall( function() return json.decode(sync_data) end )
            ---@cast data player_temp_data_for_webbert_life_eat
            if status and data and data.eat then
                for kind,num in pairs(data.eat) do
                    ThePlayer._client_data_for_webbert_life[userid].eat[kind] = (ThePlayer._client_data_for_webbert_life[userid].eat[kind] or 0) + num
                end
                TheSim:SetPersistentString(file, json.encode(ThePlayer._client_data_for_webbert_life), false)
            end
        end
    end
end)

AddClientModRPCHandler(modid,modid..'life_sync_wp',function (userid, sync_data)
    local stamp = ThePlayer and ThePlayer.replica.webbert_life and ThePlayer.replica.webbert_life:GetTimeStamp()
    if stamp then
        local file = 'webbert_life_start_at_'..stamp
        webbert_refer_persistentstring(ThePlayer)
        if ThePlayer and userid and sync_data then
            marksure_clientdata(userid)
            local status, data = pcall( function() return json.decode(sync_data) end )
            ---@cast data player_temp_data_for_webbert_life_wp
            if status and data and data.wp then
                for kind,num in pairs(data.wp) do
                    ThePlayer._client_data_for_webbert_life[userid].wp[kind] = (ThePlayer._client_data_for_webbert_life[userid].wp[kind] or 0) + num
                end
                TheSim:SetPersistentString(file, json.encode(ThePlayer._client_data_for_webbert_life), false)
            end
        end
    end
end)
-- 需要的钩子
AddComponentPostInit('harvestable',
---@param self component_harvestable
function (self)
    ---@class event_data_w_l_harvestsomething
    ---@field object ent
    ---@field num integer

    local old_Harvest = self.Harvest
    function self:Harvest(picker,...)
        local num = self.produce
        local res = old_Harvest ~= nil and {old_Harvest(self,picker,...)} or {}
        local success = res[1]
        if success then
            if picker ~= nil and picker.components.inventory ~= nil then
				picker:PushEvent("w_l_harvestsomething", { object = self.inst, num = num })
			end
        end
        return unpack(res)
    end
end)

AddComponentPostInit('trader',
---@param self component_trader
function (self)
    ---@class event_data_w_l_trade
    ---@field owner ent
    ---@field item ent

    local old_AcceptGift = self.AcceptGift
    function self:AcceptGift(giver,item,count,...)
        local res =  old_AcceptGift ~= nil and {old_AcceptGift(self,giver,item,count,...)} or {}
        if res[1] then
            if giver then
                giver:PushEvent('w_l_trade',{owner = self.inst, item = item})
            end
        end
        return unpack(res)
    end
end)

AddComponentPostInit('farmplantable',
---@param self component_farmplantable
function (self)
    local old_Plant = self.Plant
    function self:Plant(target,planter,...)
        local res = old_Plant ~= nil and {old_Plant(self,target,planter,...)} or {}
        if res[1] then
            if planter then
                planter:PushEvent('w_l_farmplant')
            end
        end
        return unpack(res)
    end
end)